package com.lolongmaps.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity implements LocationListener {

	private GoogleMap googleMap;
	private LatLng latlngDestination = null;
	private String googleMapDirectionURL;
	private Double destinationLat, destinationLng;
	private String destinationAddress;
	private ProgressDialog progressDialog;


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);
		
		Bundle extras = getIntent().getExtras();
		destinationAddress = extras.getString("address");
		
		Toast.makeText(this, "Location of establishments are approximate.", Toast.LENGTH_SHORT).show();
		
		if(isOnline()) {
			if (isGooglePlay()) {
				//All is good; Show Map, Markers and Draw Path
				setupMap();
			} 
		} 
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);

		return true;
	}
	
	//Checks Internet connection
	public boolean isOnline() {
		
	    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
	    NetworkInfo netInfo = cm.getActiveNetworkInfo();
	    if (netInfo != null && netInfo.isConnectedOrConnecting()) {
	        return true;
	    } else {
	    	Toast.makeText(this, "Your device is not connected to the internet.", Toast.LENGTH_LONG).show();
	    }
	    return false;
	}

	//Checks availability of google play on device
	private boolean isGooglePlay() {

		int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

		if (status == ConnectionResult.SUCCESS) {
			return (true);
		} else {
			Toast.makeText(this, "Google Play is not avaialble on your device.", Toast.LENGTH_LONG).show();
		}

		return (false);
	}

	//Initializes the map
	private void setupMap() {

		if (googleMap == null) {
			
			googleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
			
			if (googleMap != null) {

				googleMap.setMyLocationEnabled(true);

				LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

				Criteria criteria = new Criteria();
				criteria.setAccuracy(Criteria.ACCURACY_FINE);
				String provider = lm.getBestProvider(criteria, true);

				if (provider == null) {
					onProviderDisabled(provider);
				}

				Location loc = lm.getLastKnownLocation(provider);
				
				//Able to get the devices' location
				if (loc != null) {
					
					try {
						//Get Lat and Latitude of Destination -- Connects to Google Geocode to transform address to Lat and Long
						//Lat of destination is set to destinationLat and Long of destination is set to destinationLng
						//latlngDestination is used for plotting marker on map
						
						String url = makeURLForAddress(destinationAddress);
						latlngDestination = new Geocoding().execute(url).get();
						
						if(latlngDestination != null) {
							//Builds the URL to be used when connecting to Google Map Direction to be used for Drawing Path
							//Accepts Latitude and Longitude of location and Latitude and Longitude of destination
							googleMapDirectionURL = makeURL(loc.getLatitude(), loc.getLongitude(), this.getDestinationLat(), this.getDestinationLng());
							
							//Connect to Google Map Direction and draws the path
							new connectAsyncTask(googleMapDirectionURL).execute();	
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (ExecutionException e) {
						e.printStackTrace();
					}
										
					//Display markers for current location and destination
					onLocationChanged(loc);
				}
			}
		}
	}
	
	public String makeURLForAddress(String addressNot) {
		
		//Change spaces to %20
		String addressReplaced = addressNot;
		addressReplaced = addressReplaced.replaceAll(" ", "%20");
		return addressReplaced;
	}

	//Makes the URL for connecting to google maps direction
	public String makeURL (double sourcelat, double sourcelog, double destlat, double destlog ) {
		
        StringBuilder urlString = new StringBuilder();
        urlString.append("http://maps.googleapis.com/maps/api/directions/json");
        urlString.append("?origin=");// from
        urlString.append(Double.toString(sourcelat));
        urlString.append(",");
        urlString.append(Double.toString( sourcelog));
        urlString.append("&destination=");// to
        urlString.append(Double.toString( destlat));
        urlString.append(",");
        urlString.append(Double.toString( destlog));
        urlString.append("&sensor=false&mode=driving&alternatives=true");
        
        return urlString.toString();
	}
	
	public void drawPath(String result) {

	    try {
	            //Transform the string into a json object
	           final JSONObject json = new JSONObject(result);
	           JSONArray routeArray = json.getJSONArray("routes");
	           JSONObject routes = routeArray.getJSONObject(0);
	           JSONObject overviewPolylines = routes.getJSONObject("overview_polyline");
	           String encodedString = overviewPolylines.getString("points");
	           List<LatLng> list = decodePoly(encodedString);

	           for(int z = 0; z<list.size()-1;z++){
	                LatLng src = list.get(z);
	                LatLng dest = list.get(z+1);
	                Polyline line = googleMap.addPolyline(new PolylineOptions()
	                .add(new LatLng(src.latitude, src.longitude), new LatLng(dest.latitude, dest.longitude))
	                .width(5)
	                .color(Color.BLUE).geodesic(true));
	            }
	    } catch (JSONException e) {
	    }
	} 
	
	//Decodes the geopoints from within drawpath function to be used for actually drawing path
	private List<LatLng> decodePoly(String encoded) {

	    List<LatLng> poly = new ArrayList<LatLng>();
	    int index = 0, len = encoded.length();
	    int lat = 0, lng = 0;

	    while (index < len) {
	        int b, shift = 0, result = 0;
	        do {
	            b = encoded.charAt(index++) - 63;
	            result |= (b & 0x1f) << shift;
	            shift += 5;
	        } while (b >= 0x20);
	        int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
	        lat += dlat;

	        shift = 0;
	        result = 0;
	        do {
	            b = encoded.charAt(index++) - 63;
	            result |= (b & 0x1f) << shift;
	            shift += 5;
	        } while (b >= 0x20);
	        int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
	        lng += dlng;

	        LatLng p = new LatLng( (((double) lat / 1E5)),
	                 (((double) lng / 1E5) ));
	        poly.add(p);
	    }

	    return poly;
	}
	
	@Override
	public void onLocationChanged(Location loc) {
		// TODO Auto-generated method stub

		LatLng latlng = new LatLng(loc.getLatitude(), loc.getLongitude());
		googleMap.addMarker(new MarkerOptions().position(latlng).title("Current Location").icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
		googleMap.moveCamera(CameraUpdateFactory.newLatLng(latlng));
		googleMap.animateCamera(CameraUpdateFactory.zoomTo(15));

		googleMap.addMarker(new MarkerOptions().position(latlngDestination).title("Destination"));
		//googleMap.moveCamera(CameraUpdateFactory.newLatLng(latlngDestination));
		//googleMap.animateCamera(CameraUpdateFactory.zoomTo(15));
	}

	public double getDestinationLng() {
		
		return destinationLng;
	}
	
	public double getDestinationLat() {

		return destinationLat;
	}
	
	@Override
	public void onProviderDisabled(String arg0) {
		// TODO Auto-generated method stub
		Toast.makeText(this, "Can't get your location.", Toast.LENGTH_SHORT).show();
	}

	
	@Override
	public void onProviderEnabled(String arg0) {
		// TODO Auto-generated method stub
	}

	
	@Override
	public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
		// TODO Auto-generated method stub

	}
		
	//AsyncTask class that gets the Destinations Longitude and Latitude
	//Accepts String "Address" which is the address of the destination
	public class Geocoding extends AsyncTask<String, Void, LatLng> {

		@Override
		protected LatLng doInBackground(String... address) {
			
			HttpGet httpGet = new HttpGet("http://maps.google.com/maps/api/geocode/json?address=" + address[0] + "&sensor=false");
	        HttpClient client = new DefaultHttpClient();
	        HttpResponse response;
	        StringBuilder stringBuilder = new StringBuilder();

	        try {
	            response = client.execute(httpGet);
	            HttpEntity entity = response.getEntity();
	            InputStream stream = entity.getContent();
	            int b;
	            while ((b = stream.read()) != -1) {
	                stringBuilder.append((char) b);
	            }
	        } catch (ClientProtocolException e) {
	        } catch (IOException e) {
	        }

	        JSONObject jsonObject = new JSONObject();

	        try {
	        	
				jsonObject = new JSONObject(stringBuilder.toString());
				
				JSONObject location = jsonObject.getJSONArray("results").getJSONObject(0).getJSONObject("geometry").getJSONObject("location");
				destinationLat = location.getDouble("lat");
				destinationLng = location.getDouble("lng");
	        	
	            latlngDestination = new LatLng(destinationLat, destinationLng);
	            //this.onPostExecute(latlngDestination);
	            
	            
	        } catch (JSONException e) {
	            e.printStackTrace();
	        } catch (IllegalStateException e) {
				e.printStackTrace();
			} 
	        
			return latlngDestination;
		}
		
		protected void onPostExecute(Long result) {
			googleMap.addMarker(new MarkerOptions().position(latlngDestination).title("Destination"));
	     }
	}

	
	//AsyncTask Class that connects to and parses the result from the Google Maps Direction service site
	//and Draws the Path from Location to Destination
	//Accepts String "URL" got from Geocoding AsyncTask 
	private class connectAsyncTask extends AsyncTask<Void, Void, String> {
		
	    String url;
	    
	    connectAsyncTask(String urlPass) {
	    	
	        url = urlPass;
	    }
	    
	    @Override
	    protected void onPreExecute() {
	        // TODO Auto-generated method stub
	        super.onPreExecute();
	        progressDialog = new ProgressDialog(MainActivity.this);
	        progressDialog.setMessage("Fetching route, Please wait...");
	        progressDialog.setIndeterminate(true);
	        progressDialog.show();
	    }
	    @Override
	    protected String doInBackground(Void... params) {
	        JSONParser jParser = new JSONParser();
	        String json = jParser.getJSONFromUrl(url);
	        return json;
	    }
	    @Override
	    protected void onPostExecute(String result) {
	        super.onPostExecute(result);   
	        progressDialog.hide();        
	        if(result!=null){
	        	//Result is String "json" drawpath transforms it into JSON object 
	            drawPath(result);
	        }
	    }
	}


	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		progressDialog.dismiss();
	}
}	
	
//	public void getLatLongFromAddress(String youraddress) {
//
//        HttpGet httpGet = new HttpGet("http://maps.google.com/maps/api/geocode/json?address=" +youraddress+"&sensor=false");
//        HttpClient client = new DefaultHttpClient();
//        HttpResponse response;
//        StringBuilder stringBuilder = new StringBuilder();
//
//        try {
//            response = client.execute(httpGet);
//            HttpEntity entity = response.getEntity();
//            InputStream stream = entity.getContent();
//            int b;
//            while ((b = stream.read()) != -1) {
//                stringBuilder.append((char) b);
//            }
//        } catch (ClientProtocolException e) {
//        } catch (IOException e) {
//        }
//
//        JSONObject jsonObject = new JSONObject();
//        try {
//            jsonObject = new JSONObject(stringBuilder.toString());
//
//            addressLng = ((JSONArray)jsonObject.get("results")).getJSONObject(0)
//                .getJSONObject("geometry").getJSONObject("location")
//                .getDouble("lng");
//
//            addressLat = ((JSONArray)jsonObject.get("results")).getJSONObject(0)
//                .getJSONObject("geometry").getJSONObject("location")
//                .getDouble("lat");
//
//            //Log.d("latitude", addressLng);
//            //Log.d("longitude", addressLat);
//        } catch (JSONException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
//    }
